package area_ext

import (
	"errors"

	"gddgame.cc/galaxy/binocle"
	"gddgame.cc/galaxy/binocle/module/socket"
	"gddgame.cc/galaxy/common/dsl/game/area"
	"gddgame.cc/galaxy/common/feature/orbit/sync_protocol"
	"gddgame.cc/galaxy/core/sdk"
	"gddgame.cc/galaxy/satellite/formula/nebula"
	"gddgame.cc/galaxy/server/composer"
)

// Area Namespace
const (
	Join nebula.NSE = iota
	Leave
	Auth
	Load
	Edit
	Process

	OnJoin = 127
)

type AreaType byte

const (
	SmallAreaType AreaType = iota
	WorldAreaType
)

type AreaConfig struct {
	AreaId         string
	CanEdit        bool
	NeedSync       bool
	UpdateLocation bool
	Type           AreaType
	Identity       string
}

type AreaManager interface {
	Depends() error
}

type AreaDelegate interface {
	GetAccessTokenPolicy(namespace string, token string) map[string]interface{}
	GetSyncAreaConfig(config AreaConfig) *sync_protocol.AreaConfig
	GetAreaInfo(openId string, token string) AreaConfig
	GetAreaData() *area.AreaData
}

//func (d *Delegate) GetSyncAreaConfig(config area_ext.AreaConfig) *sync_protocol.AreaConfig {
//	areaConfig := &sync_protocol.AreaConfig{
//		Area:           config.AreaId,
//		SyncMode:       sync_protocol.SingleSyncMode,
//		MaxVision:      0,
//		Gap:            0,
//		Rate:           100,
//		UpdateLocation: config.UpdateLocation,
//	}
//	if strings.HasPrefix(config.AreaId, "out") {
//		areaConfig.MaxVision = 100
//		areaConfig.Gap = 150
//	}
//	return areaConfig
//}
//func (d *Delegate) GetAreaInfo(openId string, token string) area_ext.AreaConfig {
//	config := area_ext.AreaConfig{
//		AreaId:         "",
//		CanEdit:        false,
//		NeedSync:       false,
//		UpdateLocation: false,
//		Type:           area_ext.SmallAreaType,
//		Identity:       "",
//	}
//	if token == "home" {
//		config.AreaId = openId
//		config.CanEdit = true
//	} else {
//		config.AreaId = token
//		config.CanEdit = false
//		config.NeedSync = true
//		config.Type = area_ext.WorldAreaType
//		config.UpdateLocation = true
//	}
//	return config
//}
//func (d *Delegate) GetAreaData() *area.AreaData {
//	return controller.Center.AreaData
//}

type areaService struct {
	builder composer.Builder
	binocle.App
	Sdk      *sdk.AppClient
	Delegate AreaDelegate
}

func (service *areaService) Depends() error {
	return nil
}

func AreaHandler(builder composer.Builder, namespace nebula.Namespace, client *sdk.AppClient, delegate AreaDelegate) AreaManager {
	// 注册Area服务
	var agent binocle.App
	builder.Factory("agent", &agent)

	areaManager := &areaService{builder: builder, App: agent}
	_ = builder.BindInstance("area", areaManager)

	// 绑定系统通道
	areaManager.Sdk = client
	areaManager.Delegate = delegate

	agent.ListenDo("area/location", func(req socket.Request) {
		var info sync_protocol.AreaUpdateLocation
		if err := req.Data(&info); err != nil {
			return
		}
		data := areaManager.Delegate.GetAreaData()
		_ = data.UpdateLocation(info.OpenId, info.AreaId, info.Location)
	})

	_ = namespace.Connect(func(socket *nebula.Socket, next nebula.Next) {
		token, ok := socket.Query("token")
		socket.Debugf("[ Area ] connect: %s", token)
		if !ok {
			_ = next(errors.New("Area query error"))
			return
		}
		var openId string
		if err := socket.Get("open_id", &openId); err != nil {
			_ = next(errors.New("Auth error"))
			return
		}
		auth, err := areaManager.Sdk.GenerateAccessToken(openId, areaManager.Delegate.GetAccessTokenPolicy("area", token), 1500)
		if err != nil {
			_ = next(err)
		} else {
			_ = socket.Emit(Auth, auth)
		}

		var info AreaConfig
		var areaId int
		var content []byte
		var location []byte
		_ = socket.On(Join, func(token string) {
			// 查询该area的配置信息
			info = areaManager.Delegate.GetAreaInfo(openId, token)
			if info.UpdateLocation {
				data := areaManager.Delegate.GetAreaData()
				location = data.GetLocation(openId, info.AreaId)
			}
			var b []byte
			var err error
			if info.NeedSync {
				areaConfig := areaManager.Delegate.GetSyncAreaConfig(info)
				b, err = areaConfig.MarshalMsg(nil)
				if err != nil {
					builder.Error(err)
				}
			}
			_ = socket.Emit(OnJoin, info.Type, info.CanEdit, info.NeedSync, info.UpdateLocation, b, location)
		})
		// 绑定全局area，事件绑定在socket上
		_ = socket.On(Load, func(location []byte) []byte {
			data := areaManager.Delegate.GetAreaData()
			switch info.Type {
			case SmallAreaType:
				areaId, content, _ = data.LoadSmall(info.Identity)
			case WorldAreaType:
				// 读取对应区块地图信息
				areaId, content, _ = data.LoadWorld(info.Identity, location)
			}
			return content
		})
		_ = socket.On(Edit, func(location []byte, content []byte) error {
			data := areaManager.Delegate.GetAreaData()
			if !info.CanEdit {
				return nil
			}
			switch info.Type {
			case SmallAreaType:
				return data.UpdateSmall(areaId, content)
			case WorldAreaType:
				return data.UpdateWorld(areaId, content)
			}
			return nil
		})
		_ = socket.On(Leave, func(location []byte) {
			data := areaManager.Delegate.GetAreaData()
			_ = data.UpdateLocation(openId, info.AreaId, location)
		})
		_ = socket.On(Process, func(info []byte) {

		})

		_ = socket.Disconnected(func(reason string) {

		})
	})
	return areaManager
}
